<?php
/**
 * In this file all basic framework wide exceptions a defined. This exceptions are already loaded with the framework.
 *
 * Also, here the framework used error handler is defined. It works like follow:
 *
 * - On \E_NOTICE, \E_USER_NOTICE und \E_STRICT a \UF\PhpException is only thrown, if one of the constants
 *   \DEBUG oder \UF_DEBUG and the constant \UF_NOTICES_SHOW is defined. If \UF_NOTICES_SHOW is not defined, but
 *   one of the other 2 constants, the Message do not throw a exception but gets loged.
 * - For all other error code already a {@see \UF\PhpException} is thrown.
 *
 * @category   UniKap-Framework
 * @package    Core
 * @subpackage Exceptions
 * @author     Ulf -UniKado- Kadner <ulfikado@gmail.com>
 * @since      2014-03-29 16:17
 * @version    0.1
 */

namespace UF
{

    # <editor-fold defaultstate="collapsed" desc="E X C E P T I O N  '\UF\ExceptionBase'">

    /**
     * <b>ATTENTION!</b> Never throw or use this exception inside your code!
     *
     * @since      v0.1
     * @internal   Its only for internal use
     * @ignore
     */
    class ExceptionBase extends \Exception
    {

        # <editor-fold defaultstate="collapsed" desc="- - - >   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Inits a new instance.
         *
         * @param string     $message The error message
         * @param int        $code Optional previous exception. (defauls to \E_ERROR)
         * @param \Exception $previous The optional error code.
         */
        public function __construct( $message, $code=\E_ERROR, \Exception $previous=null )
        {
            parent::__construct( $message, $code, $previous );
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Overrides __toString, to return a more detailed string if object is casted to a string.
         *
         * @return string
         */
        public function __toString()
        {
            $msg = \get_class($this) . ' ' . self::GetCodeName($this->code) .
                \UF\_2( '_core', " in [%s] at line %d\n", $this->file, $this->line ) .
                \str_replace( "\n", "\n    ", $this->message );
            $previos = $this->getPrevious();
            if ( \is_null($previos) ) { return $msg; }
            if ( $previos instanceof \UF\ExceptionBase ) {
                $msg .= "\n    " . \str_replace( "\n", "\n    ", (string)$previos );
            }
            else {
                $msg .= "\n    " . \get_class( $previos ) . ' ' . self::GetCodeName( $previos->getCode() ) .
                \UF\_2( '_core', " in [%s] at line %d\n", $previos->file, $previos->line ) . '    ' .
                \str_replace( "\n", "\n    ", $previos->message );
            }
            return $msg;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - -">

        /**
          * Returns a string, representing the defined error code.
          *
          * @param  int $code e.g.: \E_USER_ERROR
          * @return string
          */
        public static function GetCodeName($code)
        {
            switch ($code)
            {
                case \E_ERROR: return \UF\_( '_core', 'Error' );
                case \E_NOTICE: return \UF\_( '_core', 'Notice' );
                case \E_PARSE: return \UF\_( '_core', 'Parse error' );
                case \E_RECOVERABLE_ERROR: return \UF\_( '_core', 'Recoverable error' );
                case \E_STRICT: return \UF\_( '_core', 'Strict warning' );
                case \E_USER_ERROR: return \UF\_( '_core', 'Application error' );
                case \E_USER_NOTICE: return \UF\_( '_core', 'Application notice' );
                case \E_USER_WARNING: return \UF\_( '_core', 'Application warning' );
                case \E_WARNING: return \UF\_( '_core', 'Warning' );
                default: return \UF\_( '_core', 'Unknown error' );
            }
        }

        # </editor-fold>

    }

    # </editor-fold>

    # <editor-fold defaultstate="collapsed" desc="E X C E P T I O N  '\UF\PhpException'">

    /**
     * This exception is used by framework error handler. It will only be thrown by the error handler, if a
     * catchable error occours.
     *
     * <b>ATTENTION!</b> Never throw this exception inside your code!
     *
     * @since      v0.1
     */
    class PhpException extends \UF\ExceptionBase
    {

        # <editor-fold defaultstate="collapsed" desc="- - - >   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Inits a new instance.
         *
         * @param string $msg
         * @param int    $code
         * @param int    $line
         * @param string $file
         * @ignore
         */
        public function __construct( $msg, $code, $line, $file )
        {
            parent::__construct( \strip_tags($msg), $code );
            $this->file = $file;
            $this->line = $line;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Writes the current error message to PHPs error log.
         */
        public final function writeLog()
        {
            \error_log( \str_replace("\n", ' ', (string)$this), 0 );
        }

        # </editor-fold>

    }

    # </editor-fold>

    # <editor-fold defaultstate="collapsed" desc="E X C E P T I O N  '\UF\Exception'">

    /**
     * This Exception defines the base functionality for all other Framework excptions.
     *
     * @since      v0.1
     */
    class Exception extends \UF\ExceptionBase
    {

        # <editor-fold defaultstate="collapsed" desc="C O N S T R U C T O R">

        /**
          * Inits a new instance.
          *
          * @param string     $message Fehlermeldung
          * @param int        $code The optional Error code (default to \E_ERROR)
          * @param \Exception $previous A optional previous exception
          */
        public function __construct( $message, $code=\E_ERROR, \Exception $previous=null )
        {
            parent::__construct( $message, $previous, $code );
            \error_log( \str_replace("\n", ' ', (string)$this), 0 );
        }

        # </editor-fold>

    }

    # </editor-fold>

    # <editor-fold defaultstate="collapsed" desc="E X C E P T I O N   &   E R R O R - H A N D L E R">

    /**
     * @access private
     * @internal This is the internal framework error handler
     * @ignore
     */
    function error_handler( $errno, $errstr, $errfile, $errline )
    {
        switch ($errno)
        {
            case \E_NOTICE:
            case \E_USER_NOTICE:
            case \E_STRICT:
                if ( !\defined('DEBUG') && !\defined('UF_DEBUG') ) { break; }
                if ( !\defined('UF_NOTICES_SHOW') ) {
                    throw new \UF\PhpException( $errstr, $errno, $errline, $errfile ); }
                else { new \UF\PhpException( $errstr, $errno, $errline, $errfile ); }
                break;
            default:
                throw new \UF\PhpException( $errstr, $errno, $errline, $errfile );
        }
    }

    \set_error_handler( '\\UF\\error_handler' );

    /**
     * @access private
     * @internal This is the internal framework exception handler
     * @ignore
     */
    function exception_handler( \Exception $exception )
    {
        if ( $exception instanceof \UF\PhpException ) $exception->writeLog();
        switch ( $exception->getCode() )
        {
            case \E_NOTICE: case \E_STRICT: case \E_USER_NOTICE:
                if ( !\defined('DEBUG') && !\defined('UF_DEBUG') ) break;
                if ( !\headers_sent() ) \header ( 'Content-Type: text/plain; charset=utf-8' );
                if ( $exception instanceof ExceptionBase )
                {
                    exit( \UF\_( '_core', 'Unhandled Application-Error: ' ) . (string)$exception . \PHP_EOL .
                          \strip_tags($exception->getTraceAsString()) );
                }
                exit( \UF\_( '_core', 'Unhandled Application-Error: ' ) . $exception->getMessage() . \PHP_EOL .
                      \strip_tags($exception->getTraceAsString()) );
            default:
                if ( !\defined('DEBUG') && !\defined('UF_DEBUG') ) {
                    exit ( \UF\_( '_core',
                        "Unhandled Application-Error.\n===============================\nThe application execution " .
                        "is canceled!" ) );
                }
                if ( $exception instanceof \UF\ExceptionBase ) {
                    exit ( \UF\_( '_core', 'Unhandled Application-Error: ' ) . (string)$exception . \PHP_EOL .
                           \strip_tags( $exception->getTraceAsString() ) ); }
                exit ( \UF\_( '_core', 'Unhandled Application-Error: ' ) . $exception->getMessage() . \PHP_EOL .
                        \strip_tags( $exception->getTraceAsString() ) ); }
    }

    if ( !\defined( 'UF_NO_EXCEPTION_HANDLER' ))
    {

        \set_exception_handler( '\\UF\\exception_handler' );

    }

    # </editor-fold>

}

